package com.bruce.spring.http.client.factory;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.JdkClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.client.support.RestTemplateAdapter;
import org.springframework.web.service.invoker.HttpExchangeAdapter;
import org.springframework.web.util.DefaultUriBuilderFactory;

import java.io.IOException;
import java.net.http.HttpClient;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * Created by bruce in 2025/6/29 01:24
 */
@Slf4j
@RequiredArgsConstructor
@Component
public class RestTemplateAdapterCreator implements HttpExchangeAdapterCreator {

    private final HttpMessageConverters messageConverters;

    // Reactor netty http client
    public HttpExchangeAdapter useReactorNettyHttpClient(String url) {
        // reactor.netty.http.client.HttpClient reactorHttpClient = reactor.netty.http.client.HttpClient.create()
        //         .protocol(HttpProtocol.H2C, HttpProtocol.HTTP11)
        //         .baseUrl(url);
        // ReactorClientHttpRequestFactory clientFactory = new ReactorClientHttpRequestFactory(reactorHttpClient);
        // return createRestTemplateAdapter(url, clientFactory);
        throw new UnsupportedOperationException();
    }

    // Jdk9+ httpClient
    public HttpExchangeAdapter useJdk9HttpClient(String url) {
        HttpClient httpClient = HttpClient.newBuilder()
                .version(HttpClient.Version.HTTP_2)
                .build();

        JdkClientHttpRequestFactory jdkClientHttpFactory = new JdkClientHttpRequestFactory(httpClient);
        return createRestTemplateAdapter(url, jdkClientHttpFactory);
    }

    // Apache HttpClient5
    public HttpExchangeAdapter useApacheHttpClient5(String url) {
        // PoolingAsyncClientConnectionManager clientConnectionManager = new PoolingAsyncClientConnectionManager();
        // clientConnectionManager.setDefaultTlsConfig(
        //         TlsConfig.custom()
        //                 .setVersionPolicy(HttpVersionPolicy.NEGOTIATE)
        //                 .build()
        // );
        // MinimalHttpAsyncClient client = HttpAsyncClients.createMinimal(clientConnectionManager);
        // CloseableHttpClient classic = HttpAsyncClients.classic(client, Timeout.ofSeconds(10000));
        // HttpComponentsClientHttpRequestFactory apacheHttpFactory = new HttpComponentsClientHttpRequestFactory(classic);
        // return createRestTemplateAdapter(url, apacheHttpFactory);
        throw new UnsupportedOperationException();
    }

    // Jdk HttpUrlConnection
    public HttpExchangeAdapter useHttpUrlConnection(String url) {
        SimpleClientHttpRequestFactory simpleClientHttpRequestFactory = new SimpleClientHttpRequestFactory();
        simpleClientHttpRequestFactory.setConnectTimeout(30000);
        return createRestTemplateAdapter(url,
                simpleClientHttpRequestFactory,
                new HttpUrlConnectionGzipRequestInterceptor(),
                new FixJdkPutMethodBugRequestInterceptor()
        );
    }


    private HttpExchangeAdapter createRestTemplateAdapter(String url,
                                                          ClientHttpRequestFactory clientHttpRequestFactory,
                                                          ClientHttpRequestInterceptor... interceptors) {
        ArrayList<ClientHttpRequestInterceptor> allInterceptors = new ArrayList<>(Arrays.asList(interceptors));
        allInterceptors.add(new RequestAuthTokenInterceptor());
        allInterceptors.add(new PrintHeaderHttpRequestInterceptor());

        RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory);
        restTemplate.setMessageConverters(messageConverters.getConverters());
        restTemplate.setUriTemplateHandler(new DefaultUriBuilderFactory(url));
        restTemplate.setInterceptors(allInterceptors);

        return RestTemplateAdapter.create(restTemplate);
    }


    private static class RequestAuthTokenInterceptor implements ClientHttpRequestInterceptor {

        @Override
        public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
            request.getHeaders().add("my-token", "token-val111");
            return execution.execute(request, body);
        }
    }

    private static class PrintHeaderHttpRequestInterceptor implements ClientHttpRequestInterceptor {

        @Override
        public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
            HttpHeaders requestHeaders = request.getHeaders();
            for (Map.Entry<String, List<String>> entry : requestHeaders.entrySet()) {
                log.info("request header:{}", entry);
            }

            ClientHttpResponse response = execution.execute(request, body);

            HttpHeaders responseHeaders = response.getHeaders();
            for (Map.Entry<String, List<String>> entry : responseHeaders.entrySet()) {
                log.info("response header:{}", entry);
            }
            return response;
        }
    }

    private static class HttpUrlConnectionGzipRequestInterceptor implements ClientHttpRequestInterceptor {

        @Override
        public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
            request.getHeaders().add("Accept-Encoding", "gzip");
            ClientHttpResponse response = execution.execute(request, body);
            HttpHeaders headers = response.getHeaders();
            List<String> headerVals = headers.get(HttpHeaders.CONTENT_ENCODING);
            if (headerVals != null && headerVals.contains("gzip")) {
                headers.remove(HttpHeaders.CONTENT_ENCODING);
                return new SimpleClientGzipHttpResponse(response);
            }
            return response;
        }
    }

    private static class FixJdkPutMethodBugRequestInterceptor implements ClientHttpRequestInterceptor {

        @Override
        public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
            if (request.getMethod().equals(HttpMethod.PUT)) {
                // 如果是PUT请求, 不使用KeepAlive, 否则JDK的HttpURLConnection会复用连接,
                // 导致服务端重启后, 客户端再次请求后失败一次
                request.getHeaders().add("Connection", "close");
            }
            return execution.execute(request, body);
        }
    }


}
